import _ from "lodash"
import { fetchLotSize } from "../moex/fetch-lot-size"
import { fetchPrice } from "../moex/fetch-price"

export interface MoexAsset {
    code: string,
    count: number,
    freeFloat: number,
    weight: number
  }
export interface PortfileAsset {
    code: string
    weight: number
    lotSize: number
    price: number
    count: number
}

export interface Investor {
    getAssets(capital: number): Promise<PortfileAsset[]>
}

export class IndexInvestor implements Investor {
    _data: MoexAsset[]

    constructor(data: MoexAsset[]) {
        this._data = data
    }

    static of(data: MoexAsset[]) {
        return new IndexInvestor(data)
    }

    async getAssets(capital: number): Promise<PortfileAsset[]> {
        const index = _.reverse(_.sortBy(this._data, row => row.weight))
        let out: PortfileAsset[] = []

        for(let i=0; i< index.length; i++) {
            const asset = index[i]
            const lotSize = await fetchLotSize(asset.code)
            const price = await fetchPrice(asset.code)
            if (!price) {
                throw new Error(`Не смог загрузить цену для ${asset.code}`)
            }

            const a: PortfileAsset = {
                code: asset.code,
                lotSize,
                price,
                weight: asset.weight,
                count: 0
            }
            const next = this.rebalance([a, ...out], capital)
            if (!this.validate(next)) {
                return _.sortBy(out, a => -1 * a.weight)
            } else {
                out = next
            }
        }

        return _.sortBy(out, a => -1 * a.weight)
    }

    validate(portfolio: PortfileAsset[]): boolean {
        for(let i=0; i<portfolio.length; i++) {
            const asset = portfolio[i]
            if (asset.count <= 0) return false
        }
        return true
    }

    rebalance(assets: PortfileAsset[], capital: number): PortfileAsset[] {
        const sum = _.sumBy(assets, a => a.weight)
        return _.map(assets, a => ({
            ...a, 
            count: Math.floor((a.weight/sum * capital) / a.price / a.lotSize) * a.lotSize
        }))
    }

}
